package token

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/spf13/viper"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

type DouYinAccessToken struct {
	AccessToken string    `json:"access_token"`
	ExpiresIn   time.Time `json:"expires_in"`
}

var DouYinToken DouYinAccessToken

type douYinTokenSuccess struct {
	Data struct {
		AccessToken string `json:"access_token"`
		Description string `json:"description"`
		ErrorCode   int    `json:"error_code"`
		ExpiresIn   int    `json:"expires_in"`
	} `json:"data"`
	Message string `json:"message"`
}

type douYinTokenError struct {
	Data struct {
		Captcha     string `json:"captcha"`
		DescUrl     string `json:"desc_url"`
		Description string `json:"description"`
		ErrorCode   int    `json:"error_code"`
	} `json:"data"`
	Message string `json:"message"`
}

// GetDouYinAccessToken
// 获取抖音的access token
func GetDouYinAccessToken() (DouYinAccessToken, error) {

	if DouYinToken.AccessToken == "" {
		return newDouYinAccessToken()
	}

	if time.Now().After(DouYinToken.ExpiresIn) {
		return newDouYinAccessToken()
	}

	return DouYinToken, nil

}

func newDouYinAccessToken() (DouYinAccessToken, error) {
	token, err := getDouYinClientToken()
	if err != nil {
		return DouYinAccessToken{}, err
	}

	DouYinToken.AccessToken = token.Data.AccessToken
	DouYinToken.ExpiresIn = time.Now().Add(time.Duration(token.Data.ExpiresIn-60) * time.Second)

	return DouYinToken, nil
}

func getDouYinClientToken() (douYinTokenSuccess, error) {
	url := viper.GetString("douyin.token.access_token_url")
	method := viper.GetString("douyin.token.method")

	var successResponse douYinTokenSuccess

	requestBody, err := json.Marshal(map[string]string{
		"client_key":    viper.GetString("douyin.token.client_key"),
		"client_secret": viper.GetString("douyin.token.client_secret"),
		"grant_type":    viper.GetString("douyin.token.grant_type"),
	})
	if err != nil {
		return successResponse, err
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(requestBody))
	if err != nil {
		return successResponse, err
	}
	req.Header.Add("Content-Type", "application/json")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return successResponse, err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return successResponse, err
	}

	if err := json.Unmarshal(body, &successResponse); err != nil {
		return successResponse, err
	}

	if successResponse.Message == "success" {
		return successResponse, nil
	}

	var errorResponse douYinTokenError
	if err := json.Unmarshal(body, &errorResponse); err != nil {
		return successResponse, err
	}

	return successResponse, errors.New("description: " + errorResponse.Data.Description + ", captcha: " + errorResponse.Data.Captcha + ", desc_url: " + errorResponse.Data.DescUrl + ", error_code: " + strconv.Itoa(errorResponse.Data.ErrorCode))
}
